In the early days of programming, each application typically was self-contained. A simple application program might comprise a single executable file (frequently denoted by the extension “.exe”), while a complex application might comprise several executables chained together. Because the executables comprising a particular application could be used only by that application, the likelihood that applications would interfere with each other was remote.
Over the years, however, the size of application files grew dramatically, encouraging the sharing of code modules by applications via dynamic linking. For example, the code modules containing the functions that make an operating system work may be shared by all the applications written to run on that operating system. A file comprising a code module that can be shared in this way is sometimes called an assembly or dynamic link library and may have the extension “.dll”.
Initially, file-sharing was not a problem. Most applications only used the dlls provided with the operating system or else used private (unshared).dlls. As operating systems evolved, additional dynamic link libraries were often created, some designed to be shared among the applications developed by the operating system vendor, and others designed to be used by all developers creating applications designed to run on the operating system. Typically, these dynamic link libraries contain groups of functions that provide a standard functionality, eliminating the need for each application to implement that functionality independently.
Initially, an application that used a particular .dll might require a separate distribution of the library containing the .dll if the .dll was not included with the operating system. Software development companies frequently update the .dlls accompanying an updated version of their operating system or application to fix bugs or to add new functionality. If a new version of a .dll appeared, even if the new .dll was used in all new builds of a product, at least some of the previous distribution disks would be likely to have an older version of the .dll. Hence, frequently an individual might own several programs each using a particular dll, each program having a different version of the .dll on its distribution disks. This state of affairs can be problematic.
For example, a user may have software that uses one version of a .dll and then install software that uses a newer version of the same .dll which does not work with the first software package. Similarly, users often reinstall software—either during a system upgrade or to change configurations. In many cases a user would install software that included an older version of a particular .dll on a system that already contained a newer version. This would cause the more recently installed version of the .dll to replace the newer version of the .dll. As soon as the user attempted to run a program that required the newer version, problems might occur ranging from operational difficulties to general protection faults.
In “Component-Solution” programming, programmers take advantage of reusable, cost-effective “off the shelf” software components that implement specific functions. “Off the shelf” software components may comprise classes, custom controls and other application programming interfaces (APIs). Use of the component-solution framework for programming has resulted in the generation of hundreds of dlls that may be shared by literally thousands of applications.
If a single .dll is missing from, present in an older version in, or present in an incompatible newer version in the library of shared assemblies installed on a computing device, an application may fail. A poorly designed installation program, user error, registration error or a change in the user's PATH environment variables are a few of the ways in which this problem can occur. This suite of problems has been collectively referred to as “Dll Hell”. Dll Hell is a problem of ongoing maintenance as well as an installation problem.
One approach to solving the problem of Dll Hell is to bind an application to a particular instance of its associated .dll. In this scenario, when a user installs a first program, a first .dll will be stored with the first program. If the user installs a second program using a different version of the same .dll, the old version of the .dll will remain and the new version of the .dll will be installed with the second program. Hence, installing a new application will have no impact on the operation of the old application. One way to accomplish this outcome is to associate a version number with each .dll. When an application is built, it is bound (typically at compile time) to a version of the dlls associated with the software that generates the executable. A consequence of this approach is that a deployed application program executable generated from a specific version of the software development software, has to have the specific version of the shared assemblies associated with that version of the software development software in order to run.
Now suppose someone has developed, for example, a collection of programs using a design time development environment version 1 which is associated with shared assemblies version 1. Suppose further that subsequently, someone installs a new version, version 2, of the design time development environment which is associated with a new version of the shared assemblies, shared assemblies version 2. Suppose the collection of programs developed using design time development environment version 1 is deployed to a number of desktops. Suppose further that a developer decides to fix a bug in one of the programs using design time environment version 2. The mere act of fixing a bug in one of the programs after installation of version 2 of the design time development environment will force an upgrade of the shared assemblies to version 2 of the shared assemblies on all the desktops running the collection of programs, because when the modified program is recompiled, it will be bound to version 2 of the shared assemblies. One can readily see the magnitude of the problem created if one considers that a particular application may be deployed to hundreds or thousands of desktops. It would be helpful, if there were a way to maintain older applications without having to upgrade the shared assemblies with which the older software is run, while still enabling an upgrade to a new version of a software development tool.